using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace Spike.ContractBuilder
{
    /// <summary>
    /// An extension of the XMLWriterTraceListener that will create a new file after a specified duration or maximum size
    /// </summary>
    public class ServiceTraceListener : XmlWriterTraceListener
    {
        public ServiceTraceListener(string filename) : base(filename) { StartTraceListener(filename); }
        public ServiceTraceListener(string filename, string name) : base(filename, name) { StartTraceListener(filename); }

        string _originalFilename = string.Empty;
        string _currentFilename = string.Empty;
        object _lock = new object();

        /// <summary>
        /// Starts the tracelistener
        /// </summary>
        /// <param name="filename"></param>
        private void StartTraceListener(string filename)
        {
            _originalFilename = filename;
            CreateNewFile();
            StartMethod(filename);
        }

        /// <summary>
        /// The initial file that is created can be delete as it does not contain any trace information
        /// </summary>
        /// <param name="filename"></param>
        private void StartMethod(string filename)
        {
            lock (_lock)
            {
                try
                {
                    File.Delete(filename);
                }
                catch (Exception ex)
                {
                    Trace.TraceWarning(string.Format("Failed to delete the file \"{0}\" during initialization.", filename));
                }
            }
        }

        /// <summary>
        /// Creates the new trace file
        /// </summary>
        private void CreateNewFile()
        {
            lock (_lock)
            {
                this.Flush();

                if (this.Writer != null)
                {
                    this.Writer.Flush();
                    this.Writer.Close();
                }

                _currentFilename = IOHelper.BuildLogFileName(_originalFilename);

                this.Writer = File.CreateText(_currentFilename);
            }
        }

        

        /// <summary>
        /// Checks the file constraints to see if a new file should be created
        /// </summary>
        private void CheckFileConstraints()
        {
            lock (_lock)
            {
                FileInfo info = new FileInfo(_currentFilename);
                TimeSpan ts = DateTime.Now.Subtract(info.CreationTime);

                if (ts.Minutes > ServiceLoggingConfiguration.Current.MaxDurationMinutes ||
                    info.Length > ServiceLoggingConfiguration.Current.MaxFileSizeBytes)
                {
                    CreateNewFile();
                }
            }
        }

        /// <summary>
        /// Serializes the arguments.
        /// </summary>
        /// <param name="args">The args.</param>
        /// <returns></returns>
        private static XmlDocumentFragment SerializeArguments(object[] args)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml("<items/>");

            XmlDocumentFragment docFrag = doc.CreateDocumentFragment();
            doc.DocumentElement.AppendChild(docFrag);

            try
            {
                foreach (object item in args)
                {
                    XmlNode node = docFrag.AppendChild(doc.CreateNode(XmlNodeType.Element, "item", string.Empty));
                    node.InnerXml = SerializationHelper.SerializeServiceObject(item);
                }
            }
            catch { }
            return docFrag;
        }

        public override void TraceData(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, object data)
        {
            CheckFileConstraints();
            base.TraceData(eventCache, source, eventType, id, data);
        }
        public override void TraceData(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, params object[] data)
        {
            CheckFileConstraints();
            base.TraceData(eventCache, source, eventType, id, data);
        }
        public override void TraceEvent(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id)
        {
            CheckFileConstraints();
            base.TraceEvent(eventCache, source, eventType, id);
        }
        public override void TraceEvent(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, string format, params object[] args)
        {
            XmlDocumentFragment docFrag = SerializeArguments(args);

            CheckFileConstraints();
            base.TraceEvent(eventCache, source, eventType, id, format, docFrag.OuterXml);
        }
        public override void TraceEvent(System.Diagnostics.TraceEventCache eventCache, string source, System.Diagnostics.TraceEventType eventType, int id, string message)
        {
            CheckFileConstraints();
            base.TraceEvent(eventCache, source, eventType, id, message);
        }
        public override void TraceTransfer(System.Diagnostics.TraceEventCache eventCache, string source, int id, string message, Guid relatedActivityId)
        {
            CheckFileConstraints();
            base.TraceTransfer(eventCache, source, id, message, relatedActivityId);
        }


        [Serializable]
        public class ServiceTraceException : Exception
        {
            public ServiceTraceException() : base() { }
            public ServiceTraceException(string message) : base(message) { }
            public ServiceTraceException(string message, Exception exception) : base(message, exception) { }
            public ServiceTraceException(SerializationInfo info, StreamingContext ctxt) : base(info, ctxt) { }
        }

    }

}